var UI = {
	directives: {
		itnernal:{}
	},
	INTERNAL : {}
};
UI.render = function(model){
	UI.INTERNAL.compile($('body'));
}
UI.INTERNAL.compile = function(element, model){
	
	
	
	
	element.children().each(function(){
		UI.INTERNAL.compile($(this), model);
	});
}

UI.INTERNAL.parseExpression = function(expressions, word){
	var result = {};
	var tokens = word.split(' ');
	
	if(tokens.length != this.expressions.length) return undefined;
	for(var tci = 0; tci < tokens.length; tci++){
		var expr = expressions[tci];
		var token = tokens[tci];
		
		if(expr.indexOf('{') == 0 && expr.indexOf('}') == expr.length -1) {
			result[expr.substring(1, expr.length -1)] = token
		} else {
			if(token != expr) return undefined;
		}
	}
	
	return result;
}



UI.directives.internal.text = {
	bind: function(model, expression){
		var template = expression.template;
		for(var ti = 0; ti < template.variables.length; ti++){
			var variable = template.variables[ti];
			var property = model.get(variable);
			
			alert('BIND!!!!!!!! Come????');
			property.bind(this);
		}
	},
	updtate: function(element, expression, operation, model){
		var template = expression.template;
		var rendered = template.render(model);
		element.text(rendered);
	}
}

// MODEL
function Model(context){
	this.context = context;
	this.extras = {};
}
Model.prototype.get = function(variable){
	var contextName = variable.contextName;
	
	if(!contextName) {
		return this.context[variable.name];
		
	} else {
		if(this.extras[contextName] == undefined) return undefined;
		return this.extras[contextName][variable.name];
	}
}

UI.prop = function(startValue) {
	var property = function(newVal){
		if(newValue) {
			for(var i = 0; i < this.listeners.length; i++){
				this.listeners[i]();
			}
			
			this.value = newVal;
		} else {
			return this.value;
		}
	}
	if(startValue) property.value = startValue;
	property.listeners = [];
	property.bind = function(callback){
		this.listeners.push(callback);
	};
	return property;
}

// TEMPLATE
UI.internal.parse = function(value) {
	var variables = [];
	var varCheck = [];
	
	var tokenType = 'text';
	var token = undefined;
	
	var tokens = [];
	
	var collectToken = function(){
		if(!token) return;
		if(tokenType == 'text') tokens.push({type: 'text', value: token }); 
		else if(tokenType == 'variable') {
			var variable = UI.INTERNAL.parseVar(token);
			tokens.push({type: 'variable', value: variable});
			if(!varCheck[token]){
				varCheck[token] ={};
				variables.push(variable);
			}
		} else if(tokenType == 'function') {
			var func = UI.INTERNAL.parseFunction(token);
			for(var vi = 0; vi < func.variables.length; vi++){
				var variable = func.variables[vi]; 
				if(!varCheck[variable]){
					varCheck[variable] ={};
					variables.push(variable);
				}
			}
			tokens.push({type: 'function', value: func });
			
		}
		
		token = '';
		tokenType = 'text';
	}
	
	for(var pi = 0; pi < value.length; pi++){
		
		var currentChar = value.charAt(pi);
		var ccNext = value.charAt(pi+1);
		
		if(currentChar == '{'){
			collectToken();
			tokenType = 'function';
			continue;
		}
		if(currentChar == '$' && ccNext == '{'){
			collectToken();
			tokenType = 'variable';
			pi++;
			continue;
		}
		if(currentChar == '}'){
			collectToken();
			continue;
		}
		
		if(!token) token = '';
		token += currentChar;
	}
	
	return new Template(variables, tokens, value);
};
UI.INTERNAL.parseVar = function(variable) {
	var contextName = undefined;
	var name = undefined;
	
	var token = '';
	for(var pi = 0; pi < variable.length; pi++){
		
		var currentChar = variable.charAt(pi);
		
		if(currentChar == '.'){
			if(token){
				contextName = token;
				token = '';
			}
			continue;
		}
		
		token += currentChar;
	}
	if(!name) name = token;
	
	return new TemplateVariable(contextName, name);
};
UI.INTERNAL.parseFunction = function(functionText) {
	var specials = ' ()+-*/%=!&|';
	
	var variablesDeclaration = '';
	var rewrittenFunctionText ='\treturn ';
	
	var variables = [];
	var varCheck = {};
	var args = [];
	
	var token;
	
	var counter = 0;
	for(var pi = 0; pi < functionText.length; pi++){
		
		var currentChar = functionText.charAt(pi);
		
		if(specials.indexOf(currentChar) != -1) {
			if(token) {
				if(UI.INTERNAL.isNumber(token) || token == 'true' || token == 'false'){
					rewrittenFunctionText += token;
					
				} else
				if(varCheck[token] === undefined){
					varCheck[token] = counter;
					variables.push(UI.INTERNAL.parseVar(token));
					rewrittenFunctionText += ('p' + counter);
					variablesDeclaration += '\tvar p' + counter + ' = ' + 'params[' + counter + '];\n';
					counter++;
				} else {
					var oldCounter = varCheck[token];
					rewrittenFunctionText += ('p' + oldCounter);
				}
				
				
				token = undefined;
			}
			rewrittenFunctionText += currentChar;
			
		} else {
			if(!token) token = '';
			token += currentChar;
		}
		

	}
	if(token) rewrittenFunctionText += token;
		
	
	var func = new Function('params', variablesDeclaration + rewrittenFunctionText);
	return new TemplateFunction(variables, func);
};



function Template(variables, tokens, original){
	this.variables = variables;
	this.tokens = tokens;
	this.original = original;
}
Template.prototype.render = function(dataContext){
	var result = '';
	for(var ti = 0; ti < this.tokens.length; ti++){
		var token = this.tokens[ti];
		if(token.type == 'variable'){
			result += token.value.resolve(dataContext);
			continue;
		} 
		if(token.type == 'function'){
			var tFunc = token.value;
			result += tFunc.invoke(dataContext);
			continue;
		} 
		if(token.type == 'text'){
			result += token.value; continue;
		} 
	}
	
	return result;
}

function TemplateVariable(contextName, name){
	this.contextName = contextName; 
	this.name = name;
	this.completeName = (contextName === undefined ? name : contextName+'.'+name);
}

function TemplateFunction(variables, func){
	this.variables = variables; 
	this.func = func;
}
TemplateFunction.prototype.invoke = function(model){
	var parameters = [];
	for(var vi = 0; vi < this.variables.length; vi++){
		parameters.push(this.variables[vi].resolve(model));
	}
	return this.func(parameters);
}
